package rest

import (
	"bytes"
	"context"
	"fmt"
	"io/ioutil"
	"net/http"
	"time"
	"wxpay/internal/conf"
	"wxpay/pkg/mcontext"
	"wxpay/pkg/merrors"
	"wxpay/pkg/util"

	"github.com/dgrijalva/jwt-go"

	"go.uber.org/zap"
)

type HttpHelper interface {
	Do(ctx context.Context, method string, timeout time.Duration, url string, body []byte, header map[string]string) (resp []byte, err error)
}

type Api struct {
	// 服务名
	Name string
	// 服务地址
	Host string
}

func (h *Api) Do(ctx context.Context, method string, timeout time.Duration, path string, body []byte, header map[string]string) (resp []byte, err error) {
	op := merrors.Op("service." + h.Name + ".api")
	l := mcontext.Logger(ctx)
	client := &http.Client{Timeout: timeout}
	req, _ := http.NewRequestWithContext(ctx, method, h.Host+path, bytes.NewBuffer(body))
	for k, v := range header {
		req.Header.Set(k, v)
	}
	rawResp, err := client.Do(req)
	if err != nil {
		return nil, merrors.E(op, merrors.Invalid, err, merrors.ErrInternal)
	} else if rawResp.StatusCode != http.StatusOK {
		switch rawResp.StatusCode {
		case 832, 839, 599:
			return nil, merrors.E(op, merrors.TimeOut, fmt.Sprintf("第三方平台调用超时"), merrors.ErrTimeOut)
		}
		return nil, merrors.E(op, merrors.Invalid, fmt.Sprintf("HTTP error: %d", rawResp.StatusCode), merrors.ErrInternal)
	}
	defer rawResp.Body.Close()
	resp, err = ioutil.ReadAll(rawResp.Body)
	if err != nil {
		return nil, merrors.E(op, merrors.Invalid, "HTTP read body error", merrors.ErrInternal)
	}
	l.Info(string(op), zap.ByteString("resp", resp))
	return
}

func createAppJWTToken() (t string, err error) {
	// Create a new token object, specifying signing method and the claims
	// you would like it to contain.
	token := jwt.NewWithClaims(jwt.SigningMethodRS256, jwt.MapClaims{
		"appid": "mall",
		"exp":   util.GetNow().Add(5 * time.Minute).Unix(),
		"iat":   util.GetNow().Unix(),
	})
	key, err := jwt.ParseRSAPrivateKeyFromPEM([]byte(conf.Conf.Keys.JWTPrivateKey))
	if err != nil {
		return "", fmt.Errorf("jwt private key format wrong %v", err)
	}
	// Sign and get the complete encoded token as a string using the secret
	t, err = token.SignedString(key)
	if err != nil {
		return "", fmt.Errorf("token sign error %v", err)
	}
	t = "Bearer " + t
	return
}

func GenApiToken() (token string, err error) {
	const op merrors.Op = "service.GenApiToken"
	t, err := createAppJWTToken()
	if err != nil {
		return "", merrors.E(op, merrors.Invalid, fmt.Sprintf("生成系统级jwt token阶段失败: %v", err), merrors.ErrInternal)
	}
	return t, nil
}
